Ext.EventManager = function()
{
	var docReadyEvent, docReadyProcId, docReadyState = false, DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari || Ext.isIE10p, E = Ext.lib.Event, D = Ext.lib.Dom, DOC = document, WINDOW = window, DOMCONTENTLOADED =
	"DOMContentLoaded", COMPLETE = 'complete', propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,

	specialElCache = [];

	function getId( el )
	{
		var id = false, i = 0, len = specialElCache.length, skip = false, o;

		if( el )
		{
			if( el.getElementById || el.navigator )
			{
				// look up the id
				for( ; i < len; ++i )
				{
					o = specialElCache[i];
					if( o.el === el )
					{
						id = o.id;
						break;
					}
				}
				if( !id )
				{
					// for browsers that support it, ensure that give the el the same id
					id = Ext.id(el);
					specialElCache.push(
					{
						id: id,
						el: el
					});
					skip = true;
				}
			}
			else
			{
				id = Ext.id(el);
			}
			if( !Ext.elCache[id] )
			{
				Ext.Element.addToCache(new Ext.Element(el), id);
				if( skip )
				{
					Ext.elCache[id].skipGC = true;
				}
			}
		}
		return id;
	}

	// / There is some jquery work around stuff here that isn't needed in Ext Core.
	function addListener( el, ename, fn, task, wrap, scope )
	{
		el = Ext.getDom(el);
		var id = getId(el), es = Ext.elCache[id].events, wfn;

		wfn = E.on(el, ename, wrap);
		es[ename] = es[ename] || [];

		/*
		 * 0 = Original Function, 1 = Event Manager Wrapped Function, 2 = Scope, 3 = Adapter Wrapped Function, 4 = Buffered Task
		 */
		es[ename].push(
		[
		    fn, wrap, scope, wfn, task
		]);

		// this is a workaround for jQuery and should somehow be removed from Ext Core in the future
		// without breaking ExtJS.

		// workaround for jQuery
		if( el.addEventListener && ename == "mousewheel" )
		{
			var args =
			[
			    "DOMMouseScroll", wrap, false
			];
			el.addEventListener.apply(el, args);
			Ext.EventManager.addListener(WINDOW, 'unload', function()
			{
				el.removeEventListener.apply(el, args);
			});
		}

		// fix stopped mousedowns on the document
		if( el == DOC && ename == "mousedown" )
		{
			Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
		}
	}

	function doScrollChk()
	{
		/*
		 * Notes: 'doScroll' will NOT work in a IFRAME/FRAMESET. The method succeeds but, a DOM query done immediately after -- FAILS.
		 */
		if( window != top )
		{
			return false;
		}

		try
		{
			DOC.documentElement.doScroll('left');
		}
		catch (e)
		{
			return false;
		}

		fireDocReady();
		return true;
	}
	function checkReadyState( e )
	{

		if( Ext.isIE9m && doScrollChk() )
		{
			return true;
		}
		if( DOC.readyState == COMPLETE )
		{
			fireDocReady();
			return true;
		}
		docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
		return false;
	}

	var styles;
	function checkStyleSheets( e )
	{
		styles || (styles = Ext.query('style, link[rel=stylesheet]'));
		if( styles.length == DOC.styleSheets.length )
		{
			fireDocReady();
			return true;
		}
		docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
		return false;
	}

	function OperaDOMContentLoaded( e )
	{
		DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false);
		checkStyleSheets();
	}

	function fireDocReady( e )
	{
		if( !docReadyState )
		{
			docReadyState = true; // only attempt listener removal once

			if( docReadyProcId )
			{
				clearTimeout(docReadyProcId);
			}
			if( DETECT_NATIVE )
			{
				DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
			}
			if( Ext.isIE9m && checkReadyState.bindIE )
			{ // was this was actually set ??
				DOC.detachEvent('onreadystatechange', checkReadyState);
			}
			E.un(WINDOW, "load", arguments.callee);
		}
		if( docReadyEvent && !Ext.isReady )
		{
			Ext.isReady = true;
			docReadyEvent.fire();
			docReadyEvent.listeners = [];
		}

	}

	function initDocReady()
	{
		docReadyEvent || (docReadyEvent = new Ext.util.Event());
		if( DETECT_NATIVE )
		{
			DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
		}
		/*
		 * Handle additional (exceptional) detection strategies here
		 */
		if( Ext.isIE9m )
		{
			// Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME
			// See if page is already loaded
			if( !checkReadyState() )
			{
				checkReadyState.bindIE = true;
				DOC.attachEvent('onreadystatechange', checkReadyState);
			}

		}
		else if( Ext.isOpera )
		{
			/*
			 * Notes: Opera needs special treatment needed here because CSS rules are NOT QUITE available after DOMContentLoaded is raised.
			 */

			// See if page is already loaded and all styleSheets are in place
			(DOC.readyState == COMPLETE && checkStyleSheets()) || DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false);

		}
		else if( Ext.isWebKit )
		{
			// Fallback for older Webkits without DOMCONTENTLOADED support
			checkReadyState();
		}
		// no matter what, make sure it fires on load
		E.on(WINDOW, "load", fireDocReady);
	}

	function createTargeted( h, o )
	{
		return function()
		{
			var args = Ext.toArray(arguments);
			if( o.target == Ext.EventObject.setEvent(args[0]).target )
			{
				h.apply(this, args);
			}
		};
	}

	function createBuffered( h, o, task )
	{
		return function( e )
		{
			// create new event object impl so new events don't wipe out properties
			task.delay(o.buffer, h, null,
			[
				new Ext.EventObjectImpl(e)
			]);
		};
	}

	function createSingle( h, el, ename, fn, scope )
	{
		return function( e )
		{
			Ext.EventManager.removeListener(el, ename, fn, scope);
			h(e);
		};
	}

	function createDelayed( h, o, fn )
	{
		return function( e )
		{
			var task = new Ext.util.DelayedTask(h);
			if( !fn.tasks )
			{
				fn.tasks = [];
			}
			fn.tasks.push(task);
			task.delay(o.delay || 10, h, null,
			[
				new Ext.EventObjectImpl(e)
			]);
		};
	}

	function listen( element, ename, opt, fn, scope )
	{
		var o = (!opt || typeof opt == "boolean") ? {} : opt, el = Ext.getDom(element), task;

		fn = fn || o.fn;
		scope = scope || o.scope;

		if( !el )
		{
			throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
		}
		function h( e )
		{
			// prevent errors while unload occurring
			if( !Ext )
			{// !window[xname]){ ==> can't we do this?
				return;
			}
			e = Ext.EventObject.setEvent(e);
			var t;

			// 莫非，这里实现了事件委托机制
			if( o.delegate )
			{
				if( !(t = e.getTarget(o.delegate, el)) )
				{
					return;
				}
			}
			else
			{
				t = e.target;
			}

			if( o.stopEvent )
			{
				e.stopEvent();
			}
			if( o.preventDefault )
			{
				e.preventDefault();
			}
			if( o.stopPropagation )
			{
				e.stopPropagation();
			}

			// 如果配置{normalized:false}，那么传入的e为不包装的原生浏览器事件对象
			if( o.normalized === false )
			{
				e = e.browserEvent;
			}

			fn.call(scope || el, e, t, o);
		}

		if( o.target )
		{
			h = createTargeted(h, o);
		}
		if( o.delay )
		{
			h = createDelayed(h, o, fn);
		}
		if( o.single )
		{
			h = createSingle(h, el, ename, fn, scope);
		}
		if( o.buffer )
		{
			task = new Ext.util.DelayedTask(h);
			h = createBuffered(h, o, task);
		}

		addListener(el, ename, fn, task, h, scope);
		return h;
	}

	// ////////////////////////////////////////////////////////////////////////////////////

	Ext.EventManager.addListener(this.dom, 'click', function()
	{
	}, this, {});

	Ext.EventManager.addListener(this.dom,
	{
		'click': function()
		{
		},
		scope: this
	});

	// //////////////////////////////////////////////////////////////////////////////////

	var pub =
	{
		addListener: function( element, eventName, fn, scope, options )
		{
			if( typeof eventName == 'object' )
			{
				var o = eventName, e, val;
				for( e in o )
				{
					val = o[e];
					if( !propRe.test(e) )
					{
						if( Ext.isFunction(val) )
						{
							// shared options
							listen(element, e, o, val, o.scope);
						}
						else
						{
							// individual options
							listen(element, e, val);
						}
					}
				}
			}
			else
			{
				listen(element, eventName, options, fn, scope);
			}
		},

		removeListener: function( el, eventName, fn, scope )
		{
			el = Ext.getDom(el);
			var id = getId(el), f = el && (Ext.elCache[id].events)[eventName] || [], wrap, i, l, k, len, fnc;

			for( i = 0, len = f.length; i < len; i++ )
			{

				/*
				 * 0 = Original Function, 1 = Event Manager Wrapped Function, 2 = Scope, 3 = Adapter Wrapped Function, 4 = Buffered Task
				 */
				if( Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope) )
				{
					if( fnc[4] )
					{
						fnc[4].cancel();
					}
					k = fn.tasks && fn.tasks.length;
					if( k )
					{
						while(k--)
						{
							fn.tasks[k].cancel();
						}
						delete fn.tasks;
					}
					wrap = fnc[1];
					E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);

					// jQuery workaround that should be removed from Ext Core
					if( wrap && el.addEventListener && eventName == "mousewheel" )
					{
						el.removeEventListener("DOMMouseScroll", wrap, false);
					}

					// fix stopped mousedowns on the document
					if( wrap && el == DOC && eventName == "mousedown" )
					{
						Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
					}

					f.splice(i, 1);
					if( f.length === 0 )
					{
						delete Ext.elCache[id].events[eventName];
					}
					for( k in Ext.elCache[id].events )
					{
						return false;
					}
					Ext.elCache[id].events = {};
					return false;
				}
			}
		},

		removeAll: function( el )
		{
			el = Ext.getDom(el);
			var id = getId(el), ec = Ext.elCache[id] || {}, es = ec.events || {}, f, i, len, ename, fn, k, wrap;

			for( ename in es )
			{
				if( es.hasOwnProperty(ename) )
				{
					f = es[ename];
					/*
					 * 0 = Original Function, 1 = Event Manager Wrapped Function, 2 = Scope, 3 = Adapter Wrapped Function, 4 = Buffered Task
					 */
					for( i = 0, len = f.length; i < len; i++ )
					{
						fn = f[i];
						if( fn[4] )
						{
							fn[4].cancel();
						}
						if( fn[0].tasks && (k = fn[0].tasks.length) )
						{
							while(k--)
							{
								fn[0].tasks[k].cancel();
							}
							delete fn.tasks;
						}
						wrap = fn[1];
						E.un(el, ename, E.extAdapter ? fn[3] : wrap);

						// jQuery workaround that should be removed from Ext Core
						if( el.addEventListener && wrap && ename == "mousewheel" )
						{
							el.removeEventListener("DOMMouseScroll", wrap, false);
						}

						// fix stopped mousedowns on the document
						if( wrap && el == DOC && ename == "mousedown" )
						{
							Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
						}
					}
				}
			}
			if( Ext.elCache[id] )
			{
				Ext.elCache[id].events = {};
			}
		},

		getListeners: function( el, eventName )
		{
			el = Ext.getDom(el);
			var id = getId(el), ec = Ext.elCache[id] || {}, es = ec.events || {}, results = [];
			if( es && es[eventName] )
			{
				return es[eventName];
			}
			else
			{
				return null;
			}
		},

		removeFromSpecialCache: function( o )
		{
			var i = 0, len = specialElCache.length;

			for( ; i < len; ++i )
			{
				if( specialElCache[i].el == o )
				{
					specialElCache.splice(i, 1);
				}
			}
		},

		purgeElement: function( el, recurse, eventName )
		{
			el = Ext.getDom(el);
			var id = getId(el), ec = Ext.elCache[id] || {}, es = ec.events || {}, i, f, len;
			if( eventName )
			{
				if( es && es.hasOwnProperty(eventName) )
				{
					f = es[eventName];
					for( i = 0, len = f.length; i < len; i++ )
					{
						Ext.EventManager.removeListener(el, eventName, f[i][0]);
					}
				}
			}
			else
			{
				Ext.EventManager.removeAll(el);
			}
			if( recurse && el && el.childNodes )
			{
				for( i = 0, len = el.childNodes.length; i < len; i++ )
				{
					Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
				}
			}
		},

		_unload: function()
		{
			var el;
			for( el in Ext.elCache )
			{
				Ext.EventManager.removeAll(el);
			}
			delete Ext.elCache;
			delete Ext.Element._flyweights;

			// Abort any outstanding Ajax requests
			var c, conn, tid, ajax = Ext.lib.Ajax;
			(typeof ajax.conn == 'object') ? conn = ajax.conn : conn = {};
			for( tid in conn )
			{
				c = conn[tid];
				if( c )
				{
					ajax.abort(
					{
						conn: c,
						tId: tid
					});
				}
			}
		},
		onDocumentReady: function( fn, scope, options )
		{
			if( Ext.isReady )
			{ // if it already fired or document.body is present
				docReadyEvent || (docReadyEvent = new Ext.util.Event());
				docReadyEvent.addListener(fn, scope, options);
				docReadyEvent.fire();
				docReadyEvent.listeners = [];
			}
			else
			{
				if( !docReadyEvent )
				{
					initDocReady();
				}
				options = options || {};
				options.delay = options.delay || 1;
				docReadyEvent.addListener(fn, scope, options);
			}
		},

		fireDocReady: fireDocReady
	};
	pub.on = pub.addListener;
	pub.un = pub.removeListener;

	pub.stoppedMouseDownEvent = new Ext.util.Event();
	return pub;
}();

Ext.onReady = Ext.EventManager.onDocumentReady;

Ext.apply(Ext.EventManager, function()
{
	var resizeEvent, 
	resizeTask, 
	textEvent, 
	textSize, 
	D = Ext.lib.Dom, 
	propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/, 
	unload = Ext.EventManager._unload, 
	curWidth = 0, 
	curHeight = 0,
	// note 1: IE fires ONLY the keydown event on specialkey autorepeat
	// note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
	// (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
	useKeydown = Ext.isWebKit ? Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 : !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);

	var ret =
	{
		_unload: function()
		{
			Ext.EventManager.un(window, "resize", this.fireWindowResize, this);
			unload.call(Ext.EventManager);
		},

		// private
		doResizeEvent: function()
		{
			var h = D.getViewHeight(), w = D.getViewWidth();

			// whacky problem in IE where the resize event will fire even though the w/h are the same.
			if( curHeight != h || curWidth != w )
			{
				resizeEvent.fire(curWidth = w, curHeight = h);
			}
		},

		/**
		 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds), passes new viewport width and
		 * height to handlers.
		 * 
		 * @param {Function} fn The handler function the window resize event invokes.
		 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
		 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
		 */
		onWindowResize: function( fn, scope, options )
		{
			if( !resizeEvent )
			{
				resizeEvent = new Ext.util.Event();
				resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
				Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
			}
			resizeEvent.addListener(fn, scope, options);
		},

		// exposed only to allow manual firing
		fireWindowResize: function()
		{
			if( resizeEvent )
			{
				resizeTask.delay(100);
			}
		},

		/**
		 * Adds a listener to be notified when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
		 * 
		 * @param {Function} fn The function the event invokes.
		 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
		 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
		 */
		onTextResize: function( fn, scope, options )
		{
			if( !textEvent )
			{
				textEvent = new Ext.util.Event();
				var textEl = new Ext.Element(document.createElement('div'));
				textEl.dom.className = 'x-text-resize';
				textEl.dom.innerHTML = 'X';
				textEl.appendTo(document.body);
				textSize = textEl.dom.offsetHeight;
				setInterval(function()
				{
					if( textEl.dom.offsetHeight != textSize )
					{
						textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
					}
				}, this.textResizeInterval);
			}
			textEvent.addListener(fn, scope, options);
		},

		/**
		 * Removes the passed window resize listener.
		 * 
		 * @param {Function} fn The method the event invokes
		 * @param {Object} scope The scope of handler
		 */
		removeResizeListener: function( fn, scope )
		{
			if( resizeEvent )
			{
				resizeEvent.removeListener(fn, scope);
			}
		},

		// private
		fireResize: function()
		{
			if( resizeEvent )
			{
				resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
			}
		},

		/**
		 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
		 */
		textResizeInterval: 50,

		/**
		 * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
		 */
		ieDeferSrc: false,

		// protected, short accessor for useKeydown
		getKeyEvent: function()
		{
			return useKeydown ? 'keydown' : 'keypress';
		},

		// protected for use inside the framework
		// detects whether we should use keydown or keypress based on the browser.
		useKeydown: useKeydown
	};

	return ret;
}());

Ext.EventManager.on = Ext.EventManager.addListener;

(function()
{
	var initExtCss = function()
	{
		// find the body element
		var bd = document.body || document.getElementsByTagName('body')[0];
		if( !bd )
		{
			return false;
		}

		var cls = [];

		if( Ext.isIE )
		{
			// Only treat IE9 and less like IE in the css
			if( !Ext.isIE10p )
			{
				cls.push('ext-ie');
			}
			if( Ext.isIE6 )
			{
				cls.push('ext-ie6');
			}
			else if( Ext.isIE7 )
			{
				cls.push('ext-ie7', 'ext-ie7m');
			}
			else if( Ext.isIE8 )
			{
				cls.push('ext-ie8', 'ext-ie8m');
			}
			else if( Ext.isIE9 )
			{
				cls.push('ext-ie9', 'ext-ie9m');
			}
			else if( Ext.isIE10 )
			{
				cls.push('ext-ie10');
			}
		}

		if( Ext.isGecko )
		{
			if( Ext.isGecko2 )
			{
				cls.push('ext-gecko2');
			}
			else
			{
				cls.push('ext-gecko3');
			}
		}

		if( Ext.isOpera )
		{
			cls.push('ext-opera');
		}

		if( Ext.isWebKit )
		{
			cls.push('ext-webkit');
		}

		if( Ext.isSafari )
		{
			cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
		}
		else if( Ext.isChrome )
		{
			cls.push("ext-chrome");
		}

		if( Ext.isMac )
		{
			cls.push("ext-mac");
		}
		if( Ext.isLinux )
		{
			cls.push("ext-linux");
		}

		// add to the parent to allow for selectors like ".ext-strict .ext-ie"
		if( Ext.isStrict || Ext.isBorderBox )
		{
			var p = bd.parentNode;
			if( p )
			{
				if( !Ext.isStrict )
				{
					Ext.fly(p, '_internal').addClass('x-quirks');
					if( Ext.isIE9m && !Ext.isStrict )
					{
						Ext.isIEQuirks = true;
					}
				}
				Ext.fly(p, '_internal').addClass(((Ext.isStrict && Ext.isIE) || (!Ext.enableForcedBoxModel && !Ext.isIE)) ? ' ext-strict' : ' ext-border-box');
			}
		}
		// Forced border box model class applied to all elements. Bypassing javascript based box model adjustments
		// in favor of css. This is for non-IE browsers.
		if( Ext.enableForcedBoxModel && !Ext.isIE )
		{
			Ext.isForcedBorderBox = true;
			cls.push("ext-forced-border-box");
		}

		Ext.fly(bd, '_internal').addClass(cls);
		return true;
	};

	if( !initExtCss() )
	{
		Ext.onReady(initExtCss);
	}
})();

// Code used to detect certain browser feature/quirks/bugs at startup.
(function()
{
	/**
	 * @class Ext.supports
	 * @ignore
	 */
	var supports = Ext.apply(Ext.supports,
	{
		/**
		 * In Webkit, there is an issue with getting the margin right property, see https://bugs.webkit.org/show_bug.cgi?id=13343
		 */
		correctRightMargin: true,

		/**
		 * Webkit browsers return rgba(0, 0, 0) when a transparent color is used
		 */
		correctTransparentColor: true,

		/**
		 * IE uses styleFloat, not cssFloat for the float property.
		 */
		cssFloat: true
	});

	var supportTests = function()
	{
		var div = document.createElement('div'), doc = document, view, last;

		div.innerHTML = '<div style="height:30px;width:50px;"><div style="height:20px;width:20px;"></div></div><div style="float:left;background-color:transparent;">';
		doc.body.appendChild(div);
		last = div.lastChild;

		if( (view = doc.defaultView) )
		{
			if( view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px' )
			{
				supports.correctRightMargin = false;
			}
			if( view.getComputedStyle(last, null).backgroundColor != 'transparent' )
			{
				supports.correctTransparentColor = false;
			}
		}
		supports.cssFloat = !!last.style.cssFloat;
		doc.body.removeChild(div);
	};

	if( Ext.isReady )
	{
		supportTests();
	}
	else
	{
		Ext.onReady(supportTests);
	}
})();
